package com.abso.mp3tunes.locker.ui.actions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

import com.abso.mp3tunes.locker.core.data.Album;
import com.abso.mp3tunes.locker.core.data.Artist;
import com.abso.mp3tunes.locker.core.data.Playlist;
import com.abso.mp3tunes.locker.core.data.Track;
import com.abso.mp3tunes.locker.ui.LockerPlugin;
import com.abso.mp3tunes.locker.ui.viewers.LockerViewer;
import com.abso.mp3tunes.locker.ui.viewers.LockerViewerContentProvider;

/**
 * An abstract base class for actions performed on a Locker tree viewer.
 */
public abstract class AbstractLockerViewerAction extends Action {

	/** The Locker tree viewer this action is based on. */
	private LockerViewer lockerViewer;

	/**
	 * Constructs a new action.
	 * 
	 * @param label
	 *            the action's text.
	 * @param imageDescriptor
	 *            the action's image.
	 * @param lockerViewer
	 *            the Locker tree viewer this action is based on.
	 */
	public AbstractLockerViewerAction(String label,
			ImageDescriptor imageDescriptor, LockerViewer lockerViewer) {
		super(label, imageDescriptor);
		this.lockerViewer = lockerViewer;
	}

	/**
	 * Returns the Locker tree viewer this action is based on.
	 * 
	 * @return the Locker viewer.
	 */
	protected LockerViewer getLockerViewer() {
		return lockerViewer;
	}

	/**
	 * Extracts tracks from a list of Locker data objects (which includes
	 * artists, albums, playlists, and tracks).
	 * 
	 * @param elements
	 *            a list of Locker data objects artists, albums, playlists, and
	 *            tracks).
	 * @return a set of tracks.
	 */
	protected Track[] unwrapTracks(List elements) {
		return unwrapTracks(elements, new NullProgressMonitor());
	}

	/**
	 * Extracts tracks from a list of Locker data objects (which includes
	 * artists, albums, playlists, and tracks).
	 * 
	 * @param elements
	 *            a list of Locker data objects artists, albums, playlists, and
	 *            tracks).
	 * @param monitor
	 *            a monitor showing the advancement of the process.
	 * @return a set of tracks.
	 */
	protected Track[] unwrapTracks(List elements, IProgressMonitor monitor) {
		List unwrappedTracks = new ArrayList(elements.size());
		LockerViewerContentProvider contentProvider = (LockerViewerContentProvider) lockerViewer
				.getContentProvider();
		for (Iterator i = elements.iterator(); i.hasNext();) {
			if (monitor.isCanceled()) {
				return null;
			}
			Object elem = i.next();
			if (elem instanceof Track) {
				unwrappedTracks.add(elem);
			} else if (elem instanceof Album) {
				unwrappedTracks.addAll(Arrays.asList(contentProvider
						.getChildrenSync(elem, monitor)));
			} else if (elem instanceof Artist) {
				unwrappedTracks.addAll(Arrays.asList(contentProvider
						.getChildrenSync(elem, monitor)));
			} else if (elem instanceof Playlist) {
				unwrappedTracks.addAll(Arrays.asList(contentProvider
						.getChildrenSync(elem, monitor)));
			}
		}
		if (monitor.isCanceled()) {
			return null;
		}
		try {
			return (Track[]) unwrappedTracks.toArray(new Track[0]);
		} catch (ArrayStoreException e) {
			return new Track[0];
		}
	}

	/**
	 * Causes the <i>run</i> method of the runnable to be run asynchronously
	 * within the Eclipse UI-thread.
	 * 
	 * @param runnable
	 *            the runnable being run.
	 */
	protected void runUIAsync(Runnable runnable) {
		Control control = getLockerViewer().getControl();
		if ((control != null) && !control.isDisposed()) {
			Display display = control.getDisplay();
			if (display != null) {
				display.asyncExec(runnable);
			}
		}
	}

	/**
	 * Shows an error dialog, taking care of showing it from within the Eclipse
	 * UI-thread.
	 * 
	 * @param dialogTitle
	 *            the title of the dialog.
	 * @param dialogMessage
	 *            the message to be shown.
	 * @param statusMessage
	 *            the error status message.
	 * @param t
	 *            the exception being shown.
	 */
	protected void showErrorDialog(final String dialogTitle,
			final String dialogMessage, final String statusMessage,
			final Throwable t) {
		runUIAsync(new Runnable() {
			public void run() {
				ErrorDialog.openError(LockerPlugin.getActiveWorkbenchShell(),
						dialogTitle, dialogMessage, new Status(IStatus.ERROR,
								LockerPlugin.PLUGIN_ID, 0, statusMessage, t));
			}
		});
	}

	/**
	 * Utility method returning the title and the artist's name of a specific
	 * track.
	 * 
	 * @param track
	 *            a Locker track.
	 * @return the track label.
	 */
	protected String getTrackLabel(Track track) {
		StringBuffer label = new StringBuffer();
		label.append(track.getTitle());
		label.append(" - ");
		label.append(track.getArtistName());
		return label.toString();
	}

}
